const crypto = require('crypto');
const axios = require('axios');
const fs = require('fs');
const path = require('path');

/**
 * 生成订单号
 */
const generateOrderNo = () => {
  const timestamp = Date.now().toString();
  const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
  return `${timestamp}${random}`;
};

/**
 * 生成随机字符串
 */
const generateNonceStr = () => {
  return crypto.randomBytes(16).toString('hex');
};

/**
 * 读取证书文件
 */
const loadCertificates = () => {
  const privateKey = fs.readFileSync(path.join(process.cwd(), process.env.WECHAT_PAY_KEY_PATH));
  const platformCert = fs.readFileSync(path.join(process.cwd(), process.env.WECHAT_PAY_PLATFORM_CERT_PATH));
  return { privateKey, platformCert };
};

/**
 * 生成签名
 */
const generateSignature = (method, url, timestamp, nonceStr, body = '') => {
  const { privateKey } = loadCertificates();
  const message = `${method}\n${url}\n${timestamp}\n${nonceStr}\n${body}\n`;
  
  const sign = crypto.createSign('RSA-SHA256');
  sign.update(message);
  return sign.sign(privateKey, 'base64');
};

/**
 * 创建微信支付请求头
 */
const createWechatPayHeaders = (method, url, body = '') => {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonceStr = generateNonceStr();
  const signature = generateSignature(method, url, timestamp, nonceStr, body);

  return {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': `WECHATPAY2-SHA256-RSA2048 mchid="${process.env.WECHAT_MCH_ID}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="${process.env.WECHAT_PAY_SERIAL_NO}"`,
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
  };
};

/**
 * 统一下单（预支付）
 */
const createUnifiedOrder = async (order) => {
  try {
    // 测试环境返回 mock 数据
    if (process.env.NODE_ENV === 'test') {
      return {
        prepay_id: 'mock_prepay_id_' + order.orderNo
      };
    }

    const url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
    const body = {
      appid: process.env.WECHAT_PAY_APP_ID,
      mchid: process.env.WECHAT_MCH_ID,
      description: `订单支付-${order.memberType}`,
      out_trade_no: order.orderNo,
      notify_url: process.env.WECHAT_PAY_NOTIFY_URL,
      amount: {
        total: Math.round(order.amount * 100), // 转换为分
        currency: 'CNY'
      },
      payer: {
        openid: order.user.openId
      }
    };

    const headers = createWechatPayHeaders('POST', '/v3/pay/transactions/jsapi', JSON.stringify(body));

    const response = await axios.post(url, body, { 
      headers,
      httpsAgent: new (require('https').Agent)({
        rejectUnauthorized: false
      })
    });
    
    if (response.data && response.data.prepay_id) {
      return response.data.prepay_id;
    } else {
      console.error('统一下单响应异常:', response.data);
      throw new Error('统一下单响应异常');
    }
  } catch (error) {
    console.error('统一下单失败:', error.response?.data || error.message);
    throw new Error('统一下单失败');
  }
};

/**
 * 生成微信支付参数
 */
const generatePayParams = async (order) => {
  const prepayId = await createUnifiedOrder(order);
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonceStr = generateNonceStr();
  
  // 构造签名字符串
  const message = `${process.env.WECHAT_PAY_APP_ID}\n${timestamp}\n${nonceStr}\nprepay_id=${prepayId}\n`;
  const { privateKey } = loadCertificates();
  
  // 生成签名
  const sign = crypto.createSign('RSA-SHA256');
  sign.update(message);
  const paySign = sign.sign(privateKey, 'base64');

  return {
    appId: process.env.WECHAT_PAY_APP_ID,
    timeStamp: timestamp,
    nonceStr,
    package: `prepay_id=${prepayId}`,
    signType: 'RSA',
    paySign
  };
};

/**
 * 验证微信支付通知签名
 */
const verifyNotificationSignature = (headers, body) => {
  const { platformCert } = loadCertificates();
  const timestamp = headers['wechatpay-timestamp'];
  const nonce = headers['wechatpay-nonce'];
  const signature = headers['wechatpay-signature'];
  const serial = headers['wechatpay-serial'];

  // 验证证书序列号
  if (serial !== process.env.WECHAT_PAY_SERIAL_NO) {
    throw new Error('证书序列号不匹配');
  }

  // 构造验签名串
  const message = `${timestamp}\n${nonce}\n${body}\n`;

  // 验证签名
  const verify = crypto.createVerify('RSA-SHA256');
  verify.update(message);
  return verify.verify(platformCert, signature, 'base64');
};

/**
 * 解密回调通知数据
 */
const decryptNotificationResource = (resource) => {
  const { ciphertext, associated_data, nonce } = resource;
  
  // 使用 AEAD_AES_256_GCM 算法解密
  const decipher = crypto.createDecipheriv(
    'aes-256-gcm',
    Buffer.from(process.env.WECHAT_PAY_API_V3_KEY, 'utf8'),
    Buffer.from(nonce, 'base64')
  );

  if (associated_data) {
    decipher.setAAD(Buffer.from(associated_data, 'utf8'));
  }

  const authTag = ciphertext.slice(-16);
  const encryptedData = ciphertext.slice(0, -16);

  decipher.setAuthTag(Buffer.from(authTag));

  let decrypted = decipher.update(Buffer.from(encryptedData, 'base64'), null, 'utf8');
  decrypted += decipher.final('utf8');

  return JSON.parse(decrypted);
};

/**
 * 验证支付回调通知
 * @param {Object} headers - 回调通知的请求头
 * @param {string} body - 回调通知的请求体
 * @returns {Object} 解密后的通知数据
 */
const verifyPaymentNotification = (headers, body) => {
  try {
    // 1. 验证签名
    const isSignatureValid = verifyNotificationSignature(headers, body);
    if (!isSignatureValid) {
      throw new Error('回调通知签名验证失败');
    }

    // 2. 解析请求体
    const bodyData = JSON.parse(body);

    // 3. 解密通知数据
    const decryptedData = decryptNotificationResource(bodyData.resource);

    // 4. 验证关键信息
    if (decryptedData.appid !== process.env.WECHAT_PAY_APP_ID) {
      throw new Error('回调通知 appid 不匹配');
    }
    if (decryptedData.mchid !== process.env.WECHAT_MCH_ID) {
      throw new Error('回调通知 mchid 不匹配');
    }

    // 5. 返回解密后的通知数据
    return {
      success: true,
      data: decryptedData
    };

  } catch (error) {
    console.error('验证支付回调通知失败:', error);
    return {
      success: false,
      error: error.message
    };
  }
};

module.exports = {
  generateOrderNo,
  generatePayParams,
  verifyPaymentNotification
}; 